import pandas as pd
from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday

def convert_excel_date(date_n):
    """将Excel中的日期时间戳转换为Pandas的日期时间格式"""
    if type(date_n) == int:
        delta_d = pd.Timedelta(date_n,'d')
        date_d = pd.to_datetime('1899-12-30') + delta_d
        return date_d
    elif type(date_n) == float:
        delta_d = pd.Timedelta(int(date_n),'d')
        date_d = pd.to_datetime('1899-12-30') + delta_d
        return date_d
    else:
        return date_n


class WorkDay():
    """工作日计算函数，可以计算指定日期范围内的工作日，可以处理中国国内假日及对应调休。包括春节、劳动节、国庆中秋节"""

    def __init__(self, start_date, end_date, holiday_file_path=None):
        """初始化实例
        :param start_date: 起始日期
        :param end_date:   结束日期
        :param holiday_file_path: 包含假期内容的数据文件
        """
        # 生成指定范围内的国际标准工作日
        self._start_date = start_date
        self._end_date = end_date
        self._work_day = pd.date_range(start_date, end_date, freq='B')
        self._holiday_file_path = holiday_file_path

        # 读取当年假期安排文件
        self._readHolidayFile()

        # 在工作日中加入调休日期
        find_tx_data = self._tiaoxiu.loc[
            (self._tiaoxiu['调休日期'] >= start_date) & (self._tiaoxiu['调休日期'] <= end_date)]
        if not find_tx_data.empty:
            self._tiaoxiu_datetime_series = pd.DatetimeIndex(find_tx_data['调休日期'])
            self._work_dates_add_tx = self._work_day.append(self._tiaoxiu_datetime_series)
            self._work_dates_add_tx = self._work_dates_add_tx.sort_values()
        else:
            self._work_dates_add_tx = self._work_day

        # 生成中国国内假期日历
        self._chinaHolidayCalendar()

        # 从工作日中去掉假日
        holiday_set = set(self._work_dates_add_tx) & set(self._holidays)
        self._workday_reslut = self._work_dates_add_tx.drop(holiday_set)

    def _chinaHolidayCalendar(self):
        """生成指定日期下的国内假期"""
        rules_all = []
        for index, row in self._holiday_data.iterrows():
            name = row['假期名称']
            start_date = row['起始日期']
            end_date = row['结束日期']
            days = row['天数']
            rules_one = []
            for dt in pd.date_range(start_date, end_date, freq='d'):
                rules_one.append(Holiday(name, month=dt.month, day=dt.day))
            rules_all.extend(rules_one)

        class ChinaHolidaysCalendar(AbstractHolidayCalendar):
            rules = rules_all

        china_holidays = ChinaHolidaysCalendar()
        self._holidays = china_holidays.holidays(self._start_date, self._end_date)

    def getWorkDays(self):
        return len(self._workday_reslut)

    def _readHolidayFile(self):
        if self._holiday_file_path is None:
            try:
                from importlib.resources import files  # Python 3.9+
                resource = files("datatoolsobj.tools").joinpath("holiday_plan.xlsx")
                # 读取数据文件
                self._holiday_file_path = resource
                self._holiday_data = pd.read_excel(self._holiday_file_path, sheet_name="2025年假期安排表", header=1)
                self._tiaoxiu = pd.read_excel(self._holiday_file_path, sheet_name="2025年调休安排", header=1)
            except ImportError:
                from importlib.resources import path as resource_path  # Fallback
                with resource_path("datatoolsobj.tools", "holiday_plan.xlsx") as resource:
                    self._holiday_file_path = resource
                    self._holiday_data = pd.read_excel(self._holiday_file_path, sheet_name="2025年假期安排表", header=1)
                    self._tiaoxiu = pd.read_excel(self._holiday_file_path, sheet_name="2025年调休安排", header=1)
            else:
                raise FileNotFoundError("找不到【holiday_plan.xlsx】文件")

        # 对Excel中的日期格式转换为pandas的日期格式
        self._holiday_data['起始日期'] = self._holiday_data['起始日期'].apply(convert_excel_date)
        self._holiday_data['结束日期'] = self._holiday_data['结束日期'].apply(convert_excel_date)
        self._tiaoxiu['调休日期'] = self._tiaoxiu['调休日期'].apply(convert_excel_date)

        # 处理调休内容
        hld_name = self._holiday_data[['编号', '假期名称']]
        self._tiaoxiu = self._tiaoxiu.join(hld_name.set_index('编号'), on='编号', how='left', rsuffix='_hld')
        self._tiaoxiu = self._tiaoxiu[['序号', '编号', '假期名称', '调休日期', '备注']]

if __name__ == '__main__':
    workday = WorkDay('2025-9-28','2025-10-31')
    print(workday.getWorkDays())